﻿/**
 * @file mio.h
 * @author septem (jsm920@outlook.com)
 * @brief 在 Windows 下使用 IOCP 进行 I/O多路复用，在 Linux 下使用 epoll进行 I/O多路复用
 * @version 0.1
 * @date 2025-01-12
 * 
 * @copyright Copyright (c) 2025
 * @note mio_t 为程序基本结构体（基类）
 * 程序主体结构
 * ```c
 *      // 1. 创建 mio_t*  并判断是否成功
 *      mio_t* pmio = MioMalloc(10,4);
 *      // 2. 调用 MioOpen 类函数 创建一些对象
 *      // 3. 大循环
 *      miocontext_t * pmioc = NULL;     
 *      while(pmioc = MioWait(pmioc, 1000)) // 定时
 *      {
 *          if(pmioc == MIO_EVENT_TIMEOUT)
 *          {
 *              continue;
 *          }
 *          else if(pmioc->type == XXXXXXX){}//4.其它处理
 *      }
 *      MioFree(pmio);
 * ```
 */
#ifndef _HEAD_MIO_
#define _HEAD_MIO_
#include "array.h"
#include "list.h"
#if defined(_WIN32) && defined(_MSC_VER)
// #pragma execution_character_set("utf-8")
#define WIN32_LEAN_AND_MEAN // 预处理器宏定义，它告诉编译器仅包含Windows SDK中最常用的函数和数据类型，从而减小编译时间和生成的可执行文件大小。
#include <time.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>
// Need to link with Ws2_32.lib
#pragma comment(lib, "ws2_32.lib")
#elif defined(__linux__)
#include <sys/socket.h> // struct sockaddr socket() bind() listen() 
#include <netinet/in.h> // struct sockaddr_in struct sockaddr_in6 struct in_addr struct in6_addr
#include <sys/epoll.h> // EPOLLIN
#include <pthread.h> // thread_t
#endif
#define MIO_TYPE_BASE 0x0000
#define MIO_HAS_BUF 0x0100

#ifndef NDEBUG
//带有时间格式的调试打印,只有 deubug 版才打印, release 版不会打印
int DebugPrint(const char* format, ...);
#else 
#define DebugPrint(format, ...) 
#endif

 struct mio_t;

/**
 * @brief 通讯类型
 * 
 */
enum miotype_e
{
    MIO_TYPE_TCPS =  (MIO_TYPE_BASE + 1), // TCPS 类型
    MIO_TYPE_TCP = (MIO_TYPE_BASE + 2)| MIO_HAS_BUF,
    MIO_TYPE_TCP_CONNECTING = (MIO_TYPE_BASE + 3)| MIO_HAS_BUF, //正在等待 connect 的 TCP
    MIO_TYPE_UDP = (MIO_TYPE_BASE + 4)| MIO_HAS_BUF,
    MIO_TYPE_IPC = (MIO_TYPE_BASE + 100), //进程间通信
    MIO_TYPE_TIMER = (MIO_TYPE_BASE + 101), //定时器， 毫秒级精度 ms
    MIO_TYPE_TASKER = (MIO_TYPE_BASE + 102), //任务，等待这个任务完成
};
#define MIO_EVENT_ERR (miocontext_t*)0 //系统错误，必须处理
#define MIO_EVENT_TIMEOUT (miocontext_t*)1 // 仅超时，
#define MIO_EVENT_INTR (miocontext_t*)2 // 键盘中断，
#define MIO_TCP_NETUNREACH  10051 // WSAENETUNREACH // 网络不可用，
#define MIO_TCP_TIMEOUT 10060 // WSAETIMEDOUT // 超时
#define MIO_TCP_REFUSED 10061 // WSAECONNREFUSED // 对方拒绝
#define MIO_TIMER_DELAY MIO_TYPE_BASE+ 1000 //定时器类型 延时一次,参数为 delay, period
#define MIO_TIMER_AT    MIO_TYPE_BASE+ 1001 // 设置到达某个 毫秒时间点 参数为 year, month,day,hour,minute,second,millisecond,period,
#define MIO_TIMER_YEAR  MIO_TYPE_BASE+ 1002 // 用于设置按年重复,参数为 period-year(每period-year重复),month,day,hour,minute,second，即为每年的某个本地时间点(精确到秒)
#define MIO_TIMER_MONTH MIO_TYPE_BASE+ 1003 // 用于设置按月重复,参数为 period-month(每period-year重复),day,hour,minute,second，即为每月的某个本地时间点(精确到秒)
#define MIO_TIMER_DAY   MIO_TYPE_BASE+ 1004 // 用于设置按日重复,参数为 period-day(每period-year重复),hour,minute,second，即为每日的某个本地时间点(精确到秒)
#define MIO_TIMER_WEEK  MIO_TYPE_BASE+ 1005 // 用于设置按周重复
/**
 * @brief 定时器具体的数据结构,精度win32: 0~20毫秒;linux: 0~10毫秒
 * 
 */
typedef struct miotimer_t
{
    unsigned short id;
    unsigned short type:15;
    unsigned short valid:1; // 当前有效
    unsigned long period; // 相对于添加时间需要经过的时间
    struct timespec arriveTime; // 要到达的绝对时间（从系统启动开始计算）
    // #if defined(_WIN32) && defined(_MSC_VER)
    // FILETIME arriveTime; // 到达时的本地时间，精度为 100 纳秒间隔数
    // #elif defined(__linux__)
    // struct timespec arriveTime; // 要到达的绝对时间（从系统启动开始计算）
    // #endif
}miotimer_t;

// struct _miotaskerthread_s;
struct miotasker_t;
/**
 * @brief 线程信息
 * 
 */
typedef struct miothreadinfo_t
{
    #if defined(_WIN32) && defined(_MSC_VER)
    HANDLE hThread; // 创建的线程句柄
    DWORD tid; // 线程id
    #elif defined(__linux__)
    pthread_t tid; // 创建的线程标志
    #endif
    unsigned long timeout; //超时清零，当此值达到或超过miothreadinfo_t：：pmiota->timeout时终止任务并重启线程
    struct miotasker_t* pmiota; //当前线程正在运行的任务，用于中止任务时来通知 MioWait
}miothreadinfo_t;
enum miotaskerstatus_e
{
    MIO_TASKER_STATUS_NONE = 0, // 无状态，初始化完成，可以调用 MioTaskerSet 修改
    MIO_TASKER_STATUS_WAITTING = 0x0001, // 已分配任务，等待线程执行
    MIO_TASKER_STATUS_RUNNING = 0x0002, // 正在执行任务，
    MIO_TASKER_STATUS_COMPLETE = 0x0110, // 已完成，等待清理，需要调用 MioTaskerGetFired
    MIO_TASKER_STATUS_TIMEOUT = 0x0111, //超时被中止
    MIO_TASKER_STATUS_DEL = 0x0112, // 被中止,待删除
};

typedef struct miotasker_t
{
    unsigned long id;
    unsigned long status; // 任务状态 0：无任务；1：等待执行；2：正在执行；3：执行完成，等待清理；4：超时被中止；5：被中止,待删除
    unsigned long timeout; // 超时时间，单位毫秒
    int ret; //返回值
    int (*funtasker)(void*);// 要执行的函数
    void* params; //参数
    struct miothreadinfo_t* pmti;  // 正在执行任务的线程数据 miothreadinfo_t*
}miotasker_t;

/**
 * @brief 上下文数据
 * 
 */
typedef struct miocontext_t
{
    #if defined(_WIN32) && defined(_MSC_VER)
    OVERLAPPED olRecv; // 
    enum miotype_e type; // 通讯类型，见 enum miotype_e
    SOCKET sock;
    DWORD szRecv;  //接收到的数据大小
    #elif defined(__linux__)
    enum miotype_e type; // 通讯类型，见 enum miotype_e
    int sock;
    ssize_t szRecv; //接收到的数据大小
    // unsigned int events; // 下次传递给 ev.events的参数，当调用overlaped时传递给 epoll
    #endif
    unsigned int ev; // 事件类型
    union _data{
        struct _tcps // tcps 相关
        {
            unsigned int lip; // 保存本地 IP, 用于检查 调用 MioTCPOpenS() 时是否已添加相同的 lip:port ,如果是直接返回这个 pmioc
            unsigned short lport;// 保存本地 PORT, 用于检查 调用 MioTCPOpenS() 时是否已添加相同的 lip:port ,如果是直接返回这个 pmioc
            #if defined(_WIN32) && defined(_MSC_VER)
            SOCKET sock; // AcceptEx 参数2 sAcceptSocket
            char laddr[((sizeof (struct sockaddr_in) + 16))]; //用于接收本地的地址
            char raddr[((sizeof (struct sockaddr_in) + 16))]; //用于接收对方的地址
            #elif defined(__linux__)
            struct sockaddr raddr; //用于接收对方的地址
            #endif
        }tcps;
        struct _tcp // tcp / tcpa 相关
        {
            char* pbufRecv; //读缓存大小 // 位置不要改变,减少判断
            unsigned szBufRecv; // 读缓存 // 位置不要改变,减少判断
            struct miocontext_t* pmiocTCPS;
            unsigned int rip;
            unsigned short rport;
        }tcp;
        struct _udp // udp相关
        {
            char* pbufRecv; //读缓存大小 // 位置不要改变 // 顺序应与 miocontext_t::_data::_tcp::pbufRecv 相同
            unsigned szBufRecv; // 读缓存 // 位置不要改变 // 顺序应与 miocontext_t::_data::_tcp::szBufRecv 相同
            unsigned int lip; 
            unsigned short lport; 
        }udp;// udp相关
        struct _timer // 定时器 
        {
            #if defined(_WIN32) && defined(_MSC_VER)
            struct mio_t* pmio;
            // FILETIME base;// 调用 MioTimerOpen 时的墙上时间,通过 GetSystemTimeAsFileTime 获取
            #elif defined(__linux__)
            struct timespec base;// 调用 MioTimerOpen 时的系统启动时间
            #endif
            short ID;
            miotimer_t* pmiot; //正在计时的的 miotimer_t
            array2_t arrTimer; // 时间队列 保存 miotimer_t
        }timer;// 定时器 
        struct _tasker // 任务
        {
            struct miothreadinfo_t* pmti; //线程信息
            list_t* plistTasker; //  任务链表 miotasker_t*
            unsigned long ID;
            int cntTasker; // 可并行任务总数
            #if defined(_WIN32) && defined(_MSC_VER)
            // int cntFinishing; // 当前正在等待调用MioTaskerGetFinished的任务，使用原子操作访问通过MioTaskerGetFired 返回
            #endif
        }tasker; // 任务
        // miotaskerinfo_t * ptasker; //任务
    }d;
}miocontext_t;

/**
 * @brief 对象结构
 * 
 */
typedef struct mio_t
{
    #if defined(_WIN32) && defined(_MSC_VER)
    HANDLE epfd; // IOCP 句柄
    #elif defined(__linux__)
    int epfd; // epoll 文件描述符
    #endif
    // stack_t* pstackPbuf; //保存分配的缓存指针，不保存分配的大小
    list_t* plistContext; //保存上下文 miocontext_t 
}mio_t;
/**
 * @brief 计算对象所使用的内存
 * 
 */
#define MioMemUsed(cnt) sizeof(mio_t) /*+ StackMemUsed()*/+ ListMemUsed(sizeof(miocontext_t), cnt)
/**
 * @brief 申请一个对象的mio_t内存,并初始化
 * 
 * @param maxMio 最大多路复用路数 
 * @param cntThread 同时执行 MioWait 的线程数
 * @return mio_t* 
 */
mio_t* MioMalloc(int maxMio, int cntThread);
/**
 * @brief 初始化一个对象mio_t
 * 
 * @param pmio 
 * @param maxMio 
 * @param cntThread 
 * @return mio_t* 
 */
mio_t* MioInit(mio_t* pmio,int maxMio, int cntThread);
/**
 * @brief 注销一个 mio_t 对象，释放除 mio_t 自身的所有资源
 * 
 * @param pmio 
 */
void MioUninit(mio_t* pmio);
/**
 * @brief 注销一个 mio_t 对象，释放所有资源
 * 
 * @param pmio 
 */
void MioFree(mio_t* pmio);

/**
 * @brief 等待一个事件 （可读/对方关闭连接/有连入/成功连接对方/失败连接对方）
 * 
 * @param pmio 
 * @param timeout 
 * @return miocontext_t* 
 */
miocontext_t* MioWait(mio_t* pmio, int timeout);

/**
 * @brief 设置 tcp/udp 缓存
 * 
 * @param pmio 
 * @param pmioc 
 * @param szRecvBuf 
 * @param szSendBuf 
 * @return int 
 */
int MioSetBuf(mio_t* pmio,miocontext_t* pmioc, unsigned int szRecvBuf);
int MioSetBufPtr(mio_t* pmio,miocontext_t* pmioc, char* pRecvBuf, unsigned int szRecvBuf,int mask);
int MioGetBuf(miocontext_t* pmioc,char** ppRecvBuf, unsigned int *pszRecvBuf);

/**
 * @brief 字符串格式的 ipv4转换为 网络格式的 int (Big Endian)
 * 
 * @param ipv4 
 * @return int 
 */
unsigned int MioHostIpv4(char* ipv4);
char* MioStringIpv4(unsigned int ipv4, char* str);
/**
 * @brief 新建一个 tcps
 * 
 * @param pmio 
 * @param lip 
 * @param lport 
 * @param backlog 
 * @return miocontext_t* 
 * @note tcps示例
 * ```c
 * mio_t* pmio = MioMalloc(10,1); // 假如返回非NULL
 * miocontext_t* pmioc = MioTCPSOpen(pmio, MioHostIpv4("0.0.0.0"), 2404, 5);// 假如返回非NULL
 * MioTCPSOverlapped(pmio,pmioc);
 * for(int i = 0; i!= 10; i++) {
 *      pmioc = MioWait(pmio,1000);
 *      if(pmioc == MIO_EVENT_ERR){ printf("errno:%d",errno);fflush(stdout);break;}
 *      else if(pmioc == MIO_EVENT_TIMEOUT){continue;}
 *      switch (pmioc->type) {
 *          case MIO_TYPE_TCPS:{
 *              miocontext_t* pmiocTCPA = MioTCPSAccept(pmio, pmioc);// 假如返回非NULL
 *              MioSetBuf(pmio,pmiocTCPA, 1024);
 *              MioTCPOverlapped(pmio,pmiocTCPA);
 *              MioTCPSOverlapped(pmio,pmioc);
 *          }break;
 *          case MIO_TYPE_TCP:{
 *              char* buf = NULL; int len = 0;
 *              MioTCPRecv(pmioc, &buf, &len); // 假如返回非 -1
 *              if(len == 0){
 *                  MioTCPClose(pmio,pmioc);break; //对侧关闭连接
 *              }
 *              // some code to process the recv buf
 *              MioTCPSend(pmioc, buf, len); // 同步发送相关报文
 *              MioTCPOverlapped(pmio,pmioc);
 *          }break;
 *      }
 *  }
 * MioFree(pmio);
 * ```
 */
miocontext_t* MioTCPSOpen(mio_t* pmio,unsigned int lip, unsigned short lport, int backlog);
miocontext_t* MioTCPSInit(mio_t* pmio,miocontext_t*pmioc,unsigned int lip, unsigned short lport, int backlog);
/**
 * @brief 关闭 TCPS 的 miocontext_t* 
 * 
 * @param pmio 
 * @param pmiocTCPS 
 * @return int 
 */
int MioTCPSClose(mio_t* pmio, miocontext_t* pmiocTCPS);
int MioTCPSUninit(mio_t* pmio, miocontext_t* pmiocTCPS);
/**
 * @brief 重载 TCP, 准备接收 tcps 连入
 * 
 * @param pmio 
 * @param pmioc 
 * @return int 
 */
int MioTCPSOverlapped(mio_t* pmio, miocontext_t* pmioc);
/**
 * @brief tcps 接受一个连入
 * 
 * @param pmio 
 * @param pmiocTCPS 
 * @return miocontext_t* 
 */
miocontext_t* MioTCPSAccept(mio_t* pmio, miocontext_t* pmiocTCPS);

/**
 * @brief 创建一个 tcp 绑定本地 lip:lport 并连接对方 rip:rport
 * 
 * @param pmio 
 * @param lip 
 * @param lport 
 * @param rip 
 * @param rport 
 * @return miocontext_t* 
 * @note tcps示例
 * ```c
 * mio_t* pmio = MioMalloc(10,1); // 假如返回非NULL
 * miocontext_t* pmioc = MioTCPOpen(pmio,0,0,MioHostIpv4("192.168.66.129"),2405);;// 假如返回非NULL
 * MioSetBuf(pmio,pmioc, 1024); //分配接收缓存
 * MioTCPConnect(pmio, pmioc); //启动连接对侧
 * MioTCPSOverlapped(pmio,pmioc);
 * for(int i = 0; i!= 10; i++) {
 *      pmioc = MioWait(pmio,1000);
 *      if(pmioc == MIO_EVENT_ERR){ printf("errno:%d",errno);fflush(stdout);break;}
 *      else if(pmioc == MIO_EVENT_TIMEOUT){continue;}
 *      switch (pmioc->type) {
 *          case MIO_TYPE_TCP:{
 *              char* buf = NULL; int len = 0;
 *              MioTCPRecv(pmioc, &buf, &len); // 假如返回非 -1
 *              if(len == 0){
 *                  MioTCPClose(pmio,pmioc);break; //对侧关闭连接
 *              }
 *              // some code to process the recv buf
 *              MioTCPSend(pmioc, buf, len); // 同步发送相关报文
 *              MioTCPOverlapped(pmio,pmioc);
 *          }break;
 *          case MIO_TYPE_TCP_CONNECTING: {
 *              switch (MioTCPConnectStatus(pmioc)){ // 查看连接状态
 *              case MIO_TCP_TIMEOUT:
 *                  printf("MioTCP connect MIO_TCP_TIMEOUT,%d",errno);fflush(stdout);
 *                  break;
 *              case MIO_TCP_REFUSED:
 *                  printf("MioTCP connect MIO_TCP_REFUSED,%d",errno);fflush(stdout);
 *                  break;
 *              case MIO_TCP_NETUNREACH:
 *                  printf("MioTCP connect MIO_TCP_NETUNREACH,%d",errno);fflush(stdout);
 *                  break;
 *              default:
 *                  MioTCPOverlapped(pmio, pmioc);
 *                  break;
 *              }
 *          } break;
 *      }
 *  }
 * MioFree(pmio);
 * ```
 */
miocontext_t* MioTCPOpen(mio_t* pmio,unsigned int lip, unsigned short lport,unsigned int rip, unsigned short rport);
miocontext_t* MioTCPInit(mio_t* pmio,miocontext_t*pmioc,unsigned int lip, unsigned short lport,unsigned int rip, unsigned short rport);
int MioTCPClose(mio_t* pmio, miocontext_t* pmiocTCP);
int MioTCPUninit(mio_t* pmio, miocontext_t* pmiocTCP);
/**
 * @brief 重载 TCP, 准备接收 tcp报文
 * 
 * @param pmio 
 * @param pmioc 
 * @return int 
 */
int MioTCPOverlapped(mio_t* pmio, miocontext_t* pmioc);
/**
 * @brief 获取 tcp 连接状态，win下有4种，正常/超时/拒绝/网络不可用
 * 
 * @param pmioc 
 * @return int 
 */
int MioTCPConnectStatus(miocontext_t* pmioc);
/**
 * @brief tcp connect 操作 重连远方
 * 
 * @param pmio 
 * @param pmioc 
 * @return int 
 */
int MioTCPConnect(mio_t* pmio,miocontext_t* pmioc);

/**
 * @brief 当收到 MioWait()通知时 接收数据并立即返回
 * 
 * @param pmioc 
 * @param data 
 * @param plen 
 * @return int 
 */
int MioTCPRecv(miocontext_t* pmioc, char** ppdata, int*plen);
/**
 * @brief 发送 data 数据直到结束或失败才返回(同步发送)
 * 
 * @param pmioc 
 * @param data 
 * @param len 
 * @return int 
 */
int MioTCPSend(miocontext_t* pmioc, char* data, int len);

/**
 * @brief 创建一个UDP，绑定本地 lip::lport
 * 
 * @param pmio 
 * @param lip 以太网格式IP，通过 inet_addr() 转换而来
 * @param lport 
 * @return miocontext_t* 
 * getsockopt()/setsockopt()的选项  含义                   值
    IP_MULTICAST_TTL                  设置多播组数据的TTL值    unsigned char 
    IP_ADD_MEMBERSHIP                 在指定接口上加入组播组   struct ip_mreq 
    IP_DROP_MEMBERSHIP                退出组播组              struct ip_mreq
    IP_MULTICAST_IF                   获取默认接口或设置接口   struct in_addr
    IP_MULTICAST_LOOP                 禁止组播数据回送         unsigned char
 */
miocontext_t* MioUDPOpen(mio_t* pmio,unsigned int lip, unsigned short lport);
miocontext_t* MioUDPInit(mio_t* pmio,miocontext_t*pmioc,unsigned int lip, unsigned short lport);
/**
 * @brief 关闭 UDP 套接字
 * 
 * @param pmio 
 * @param pmiocUDP 
 * @return int 
 */
int MioUDPClose(mio_t* pmio, miocontext_t* pmiocUDP);
int MioUDPUninit(mio_t* pmio, miocontext_t* pmiocUDP);
/**
 * @brief 重载 UDP，准备接收 UDP 报文
 * 
 * @param pmio 
 * @param pmioc 
 * @return int 
 */
int MioUDPOverlapped(mio_t* pmio, miocontext_t* pmioc);
/**
 * @brief 接收 UDP 数据并获取 缓存地址，数据长度，远方IP(主机格式)，远方Port(主机格式)
 * 
 * @param pmioc 
 * @param ppdata 传入准备获取缓存的地址，传入NULL,就不会获取地址
 * @param plen 传入准备获取缓存的长度，传入NULL,就不会获取缓存的长度
 * @param prip 传入准备获取远方IP，传入NULL,就不会获取缓存的长度
 * @param prport 
 * @return int 
 */
int MioUDPRecv(miocontext_t* pmioc, char** ppdata, int*plen, unsigned int* prip,unsigned short* prport);
/**
 * @brief 当调用 MioUDPConnect 之后，就可以调用此 宏函数 来 收发报文
 * 
 */
#define MioUDPRecv2(pmioc,ppdata,plen) MioTCPRecv((pmioc),(ppdata),(plen))
int MioUDPSend(miocontext_t* pmioc, char* data, int len, unsigned int rip,unsigned short rport);
#define MioUDPSend2(pmioc, data, len ) MioTCPSend((pmioc), (data), (len))
/**
 * @brief 绑定一个远方地址，udp会变成 ESTABLISHED 状态，此时 可通过 send 发送报文
 * 
 * @param pmio 
 * @param pmioc 
 * @param rip 
 * @param rport 
 * @return int 
 */
int MioUDPConnect(mio_t* pmio, miocontext_t* pmioc,unsigned int rip, unsigned short rport);
/**
 * @brief 是否允许UDP广播报文发送
 * 
 * @param pmioc 
 * @param enable 
 * @return int 
 */
int MioUDPSetBroadcast(miocontext_t* pmioc, int enable); //允许 UDP 发送广播报文 
/**
 * @brief 设置组播，允许接收 来自 mip组播的报文， 
 * 
 * @param pmioc 
 * @param mip 
 * @param lip 对应网卡的 本地IP，设置后组播 报文将会从 此网卡上走，也可以填 0
 * @param add 1 添加 0 删除
 * @return int 
 */
int MioUDPSetMulticast(miocontext_t* pmioc, unsigned int mip, unsigned int lip, int add);
/**
 * @brief 设置组播是否可以接收本地组播报文
 * 
 * @param pmioc 
 * @param loop 
 * @return int 
 */
int MioUDPSetMulticastLoop(miocontext_t* pmioc,int loop);
/**
 * @brief 设置组播的 ttl 生存时间，可防止 经交换机时组播报文因ttl被丢包
 * 
 * @param pmioc 
 * @param ttl 
 * @return int 
 */
int MioUDPSetMulticastTTL(miocontext_t* pmioc,int ttl);
/**
 * @brief 设置组播的网络接口
 * 
 * @param pmioc 
 * @param lip 对应网卡的 本地IP，设置后组播 报文将会从 此网卡上走
 * @return int 
 */
int MioUDPSetMulticastIF(miocontext_t* pmioc,unsigned int lip);
/**
 * @brief 创建一个定时器，
 * 
 * @param pmio 
 * @return miocontext_t* 
 * @note 因为使用单一定时器+数据结构保存定时时间，选一个最近到达的计时，cpu计算会产生延迟，所以精度不高。
 * windows定时器使用 createTimerQueueTimer + PostQueuedCompletionStatus 定时精度为0~20ms
 * linux 定时器使用 timerfd_create + timerfd_set + epoll_wait + read 定时精度为0~10ms
 * @note
 * ```
 *  miocontext_t* pmiocTimer = MioTimerOpen(pmio);
 *  MioTimerAdd(pmiocTimer, MIO_TIMER_DELAY, 3000, 3000);
 *  time_t tt; time(&tt); struct tm *ptm = localtime(&tt);
 *  struct tm ttm = {.tm_year=ptm->tm_year,.tm_mon=ptm->tm_mon,.tm_mday=ptm->tm_mday,
 *      .tm_hour=ptm->tm_hour,.tm_min=ptm->tm_min,.tm_sec=ptm->tm_sec,};
 *  MioTimerAdd(pmiocTimer, MIO_TIMER_AT, ttm.tm_year+1900,ttm.tm_mon+1,ttm.tm_mday,ttm.tm_hour,ttm.tm_min,ttm.tm_sec+2,0,2000);
 *  MioTimerAdd(pmiocTimer, MIO_TIMER_YEAR, 1,ttm.tm_mon+1,ttm.tm_mday,ttm.tm_hour,ttm.tm_min,ttm.tm_sec);
 *  MioTimerAdd(pmiocTimer, MIO_TIMER_MONTH, 1,ttm.tm_mday,ttm.tm_hour,ttm.tm_min,ttm.tm_sec);
 *  MioTimerAdd(pmiocTimer, MIO_TIMER_DAY, 100,ttm.tm_hour,ttm.tm_min,ttm.tm_sec);
 *  MioTimerOverlapped(pmio,pmiocTimer);
 * while(1)
 * {
 *      miocontext_t* pmiocTimer = MioWait(pmio,1000);
 *      if(pmiocTimer == MIO_EVENT_ERR){break;}
 *      if(pmiocTimer == MIO_EVENT_TIMEOUT){continue;}
 *      if(pmiocTimer->type == MIO_TYPE_TIMER)
 *      {
 *          miotimer_t * pmiot = MioTimerGetFired(pmioc); if(pmiot == NULL){break;}
 *          // some code to process timeout....
 *          MioTimerOverlapped(pmio,pmiocTimer);
 *      }
 * }
 * ```
 */
miocontext_t* MioTimerOpen(mio_t* pmio);
miocontext_t* MioTimerInit(mio_t* pmio, miocontext_t*pmiocTimer);
/**
 * @brief 关闭定时器 miocontext_t* 对象
 * 
 * @param pmio 
 * @param pmiocTimer 
 * @return int 
 */
int MioTimerClose(mio_t* pmio, miocontext_t* pmiocTimer);
int MioTimerUninit(mio_t* pmio, miocontext_t* pmiocTimer);
/**
 * @brief 重载这个定时器，如果没有有效定时，不会触发定时器，
 * 
 * @param pmioc 
 * @return int 
 */
int MioTimerOverlapped(mio_t* pmio, miocontext_t* pmioc);
/**
 * @brief 添加一个定时器
 * 
 * @param pmiocTimer 
 * @param type 
 * @param ... 
 * @return miotimer_t* 
 */
miotimer_t* MioTimerAdd(miocontext_t* pmiocTimer,int type, ...);
/**
 * @brief 重新设置一个定时器
 * 
 * @param pmiocTimer 
 * @param _pmiot 
 * @param type 
 * @param ... 
 * @return miotimer_t* 
 */
miotimer_t* MioTimerSet(miocontext_t* pmiocTimer, miotimer_t* _pmiot, int type, ...);
/**
 * @brief 删除一个 定时器
 * 
 * @param pmiocTimer 
 * @param pmiot 
 */
void MioTimerDel(miocontext_t* pmiocTimer, miotimer_t* pmiot);
/**
 * @brief 在 Wait 后获取到达时间的对 miotimer_t
 * 
 * @param pmiocTimer 
 * @return miotimer_t* 
 * @note 会对当前超时的miotimer_t* 进行处理，如果有 period 一并处理。
 */
miotimer_t* MioTimerGetFired(miocontext_t* pmiocTimer);

/**
 * @brief 申请并初始化 pmio 的 一个 tasker
 * 
 * @param pmio 
 * @param cntThread 
 * @return miocontext_t* 
 * @note
 * ```
 *  miocontext_t* pmiocTasker = MioTaskerOpen(pmio, 4);;
 *  int a = 2;  MioTaskerAdd(pmioc, printf_val,&a, 3000);
 * while(1)
 * {
 *      miocontext_t* pmiocTimer = MioWait(pmio,1000);
 *      if(pmiocTimer == MIO_EVENT_ERR){break;}
 *      if(pmiocTimer == MIO_EVENT_TIMEOUT){continue;}
 *      if(pmiocTimer->type == MIO_TYPE_TASKER )
 *      {
 *          miotasker_t* pmiocta = MioTaskerGetFired(pmioc);
 *          if(pmiocta)
 *          {
 *              DebugPrint("tasker finished,ID: %d,status:0x%04x;return: %d\n", pmiocta->id,pmiocta->status, pmiocta->ret);
 *          }
 *          MioTaskerOverlapped(pmio, pmiocTimer);
 *      }
 * }
 * ```
 */
miocontext_t* MioTaskerOpen(mio_t* pmio, unsigned int cntTasker);
/**
 * @brief 初始化已申请的miocontext_t* pmioc
 * 
 * @param pmioc 
 * @param pmio 
 * @param cntTasker 
 * @return miocontext_t* 
 */
miocontext_t* MioTaskerInit(mio_t* pmio, miocontext_t* pmioc, unsigned int cntTasker);
/**
 * @brief 注销一个miocontext_t* pmiocTasker，并从mio_t* pmio中删除 miocontext_t* pmiocTasker
 * 
 * @param pmio 
 * @param pmiocTasker 
 */
void MioTaskerClose(mio_t* pmio, miocontext_t* pmiocTasker);
/**
 * @brief 注销一个miocontext_t* pmiocTasker
 * 
 * @param pmio 
 * @param pmiocTasker 
 */
void MioTaskerUninit(mio_t* pmio, miocontext_t* pmiocTasker);
/**
 * @brief 重载miocontext_t* pmiocTasker，允许 MioWait()再次接收到 miocontext_t* pmiocTasker 事件（允许工作线程或监视线程发送信号）
 * 
 * @param pmio 
 * @param pmiocTasker 
 * @return int 
 */
int MioTaskerOverlapped(mio_t* pmio, miocontext_t* pmiocTasker);
/**
 * @brief 添加一个任务
 * 
 * @param pmiocTasker 
 * @param funtasker 
 * @param params 
 * @param timeout 
 * @return miotasker_t* 
 * @note 当 funtasker 为NULL时仅申请一个 miotasker_t*，不执行任务，后续可以使用 MioTaskerSet再次设置任务
 */
miotasker_t* MioTaskerAdd(miocontext_t* pmiocTasker,int (*funtasker)(void*),void* params, unsigned long timeout);
/**
 * @brief 设置一个任务
 * 
 * @param pmiocTasker 
 * @param pmiot 
 * @param funtasker 
 * @param params 
 * @param timeout 
 * @return miotasker_t* 
 * @note 当 funtasker 为NULL时直接返回miotasker_t*而不执行任务，
 */
miotasker_t* MioTaskerSet(miocontext_t* pmiocTasker,miotasker_t* pmiot, int (*funtasker)(void*),void* params, unsigned long timeout);
/**
 * @brief 如果任务没有完成延时删除，如果任务已完成直接删除，不会触发MioWait
 * 
 * @param pmiocTasker 
 * @param pmiota 
 */
void MioTaskerDel(miocontext_t* pmiocTasker,miotasker_t* pmiota );
/**
 * @brief 获取已触发MioWait的任务，可能是已完成或超时
 * 用于在 MioWait-MIO_TYPE_TASKER 之后调用(调用时只允许调用一次,其它时间禁止调用).
 * 
 * @param pmiocTasker 
 * @return miotasker_t* 
 */
miotasker_t* MioTaskerGetFired(miocontext_t* pmiocTasker);

#define MioTaskerGetStatus(pmiota) ((pmiota)?((pmiota)->status):-1) //获取任务的状态
#define MioTaskerGetId(pmiota) ((pmiota)?((pmiota)->id):-1) //获取任务的ID

#endif // end of define _HEAD_MIO_
